package com.jf.threadlocal;

import com.jf.threadlocal.domain.Cat;
import com.jf.threadlocal.domain.Dog;

/**
 * Created with IntelliJ IDEA
 * Description:ThreadLocal学习
 * User:22954
 * Date:2024-09-20
 * Time:15:05
 */
public class ThreadLocal_ {
    /*
    ThreadLocal是多线程环境下存储线程局部变量的机制
    ThreadLocal在每个线程内部创建一个独立副本,每个线程仅可访问属于自己的副本
    实现机制:
        每个Thread维护一个ThreadLocalMap threadLocal
            ThreadLocalMap维护一个Entry<ThreadLocal<?> k, Object v>[]的table
            Entry的key值为ThreadLocal, value虽然是Object类型,但是set(T value)约束了传入的值只能是ThreadLocal<T>的泛型类型
            threadLocal初始化为null,在每个线程第一次调用ThreadLocal的set(T value)或get()中实例化
        每个线程对ThreadLocal<T>对象调用set(T value)放置数据,get()获得数据
            一个ThreadLocal<T>对象只能关联一个数据,多次调用set(T value)会覆盖之前的数据
    set(T value) / get():
        1.无论set还是get,底层都会先获得当前线程
        2.再从当前线程获得维护的threadLocal
        3.对threadLocal的Entry[]放置数据或取出数据   key值都为this(调用的ThreadLocal<T>对象)
    对比:
        1.传统的共享变量,多个线程可以直接访问和修改同一变量
            为确保数据一致和完整,需使用synchronized关键字进行加锁,但用synchronized关键字需对多线程同步原理深入了解,否则容易出现死锁/活锁...等问题
            所有线程都能看到共享变量的最新状态,封装性不够,也增加了复杂性
        2.ThreadLocal机制下,每个线程有自己的独立副本,线程只能访问自己的副本,不会直接影响其他线程的副本
            线程天然安全,减少开发者的负担
    适用场景:
        常用于需要保存自身状态的情况
        例如在Web应用中,每个线程处理不同的用户请求,使用ThreadLocal可以方便地保存每个用户请求相关的上下文信息,如用户身份认证信息/事务状态等.这样不同的请求线程可以独立地维护自己的状态,互不干扰.
        在一些日志记录框架中,ThreadLocal可以用来保存每个线程的日志级别等配置信息,使得不同线程可以根据自己的需求进行独立的日志记录,而不需要在每次记录日志时都传递相关配置信息.
    细节:
        1.ThreadLocalMap中Entry的key值为弱引用,在TheadLocal<T>对象被销毁时,其在table内占用的空间会被回收
            在set(T value)会调用Entry的set(ThreadLocal<?> key, Object value) {
                ...
                if (e.refersTo(null)) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
                ...
            }
            在get()调用Entry的getEntry(),getEntry()调用getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
                ...
                if (e.refersTo(null))
                    expungeStaleEntry(i);
                ...
            }
            还有remove也会清除已销毁的TheadLocal<T>对象在table中占用的Entry空间
        2.在使用服务器或线程池的时候,线程可以会被复用,需手动调用remove()方法清除ThreadLocal数据,避免污染下一次线程的数据
     */
    public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
    public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();

    public static class Test implements Runnable {
        @Override
        public void run() {
            Cat cat = new Cat();
            Dog dog = new Dog();
            threadLocal1.set(cat);
            threadLocal1.set(dog);
            threadLocal2.set(cat);
            new TestService().show();
        }
    }
    public static class Test2 implements Runnable {
        @Override
        public void run() {
            Cat cat = new Cat();
            Dog dog = new Dog();
            threadLocal1.set(cat);
            threadLocal2.set(dog);
            new TestService().show();
        }
    }

    public static void main(String[] args) {
        new Thread(new Test()).start();
        new Thread(new Test2()).start();
    }
}
